﻿/*
 *  Copyright (c) 2012 Brandon Ellenberger
 *
 *  This file is part of OpSatPlan.
 *
 *  OpSatPlan is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpSatPlan is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpSatPlan.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace OpSatPlan
{
    public class Action
    {
        private ActionDefinition mDefinition;
        private ReadOnlyCollection<PddlObject> mObjects;
        private Dictionary<AtomicFormula, AtomicFormula> mPreconditions;
        private Dictionary<AtomicFormula, AtomicFormula> mAddEffects;
        private Dictionary<AtomicFormula, AtomicFormula> mDeleteEffects;
        private Dictionary<Identifier, Identifier> mIdentifiers;
		private Dictionary<int, List<Identifier>> mIdentifierMatches;
        private int? mHashCode;
        public Action(ActionDefinition definition, IList<PddlObject> args)
        {
            this.mDefinition = definition;
            this.mObjects = new ReadOnlyCollection<PddlObject>(args);
            this.mHashCode = null;
			this.mPreconditions = new Dictionary<AtomicFormula, AtomicFormula>();
			this.mAddEffects = new Dictionary<AtomicFormula, AtomicFormula>();
			this.mDeleteEffects = new Dictionary<AtomicFormula, AtomicFormula>();
			this.mIdentifiers = new Dictionary<Identifier, Identifier>();
			this.mIdentifierMatches = new Dictionary<int, List<Identifier>>();
            Dictionary<Parameter, PddlObject> objMap = new Dictionary<Parameter,PddlObject>();
            foreach (Parameter p in mDefinition.Parameters)
            {
                objMap.Add(p, args[mDefinition.Parameters.IndexOf(p)]);
            }

            AtomicFormula a;

            foreach (AtomicFormulaDefinition afd in mDefinition.Preconditions)
            {
                List<PddlObject> objs = new List<PddlObject>();
                foreach (Parameter p in afd.Parameters)
                {
                    objs.Add(objMap[p]);
                }
                a = new AtomicFormula(afd.Predicate, objs);
                this.mPreconditions.Add(a, a);
            }

            foreach (AtomicFormulaDefinition afd in mDefinition.AddEffects)
            {
                List<PddlObject> objs = new List<PddlObject>();
                foreach (Parameter p in afd.Parameters)
                {
                    objs.Add(objMap[p]);
                }
                a = new AtomicFormula(afd.Predicate, objs);
                this.mAddEffects.Add(a, a);
            }

            foreach (AtomicFormulaDefinition afd in mDefinition.DeleteEffects)
            {
                List<PddlObject> objs = new List<PddlObject>();
                foreach (Parameter p in afd.Parameters)
                {
                    objs.Add(objMap[p]);
                }
                a = new AtomicFormula(afd.Predicate, objs);
                this.mDeleteEffects.Add(a, a);
            }
			
			List<Identifier> ids;
            foreach (IdentifierDefinition idDef in mDefinition.Identifiers)
            {
                List<Identifier> subs = new List<Identifier>();
                List<PddlObject> objs = new List<PddlObject>();
                foreach (Parameter p in idDef.AtomicFormulaDefinition.Parameters)
                    objs.Add(objMap[p]);
                a = new AtomicFormula(idDef.AtomicFormulaDefinition.Predicate, objs);
                foreach (IdentifierDefinition subDef in idDef.SubIdentifiers)
                {
                    objs.Clear();
                    foreach (Parameter p in subDef.AtomicFormulaDefinition.Parameters)
                        objs.Add(objMap[p]);
                    AtomicFormula ta = new AtomicFormula(subDef.AtomicFormulaDefinition.Predicate, objs);
                    subs.Add(new Identifier(ta, subDef.Value, subDef.IdentifierType, new List<Identifier>()));
                }
				Identifier id = new Identifier(a, idDef.Value, idDef.IdentifierType, subs);
				this.mIdentifiers.Add(id, id);
				int key = this.GetKey(id);
				if (this.mIdentifierMatches.ContainsKey(key))
					ids = this.mIdentifierMatches[key];
				else
				{
					ids = new List<Identifier>();
					this.mIdentifierMatches.Add(key, ids);
				}
				ids.Add(id);
			}
        }
		private int GetKey(Identifier id)
		{
			int key = 17;
			key = key * 37 + id.AtomicFormula.Definition.GetHashCode();
			key = key * 37 + id.Value.GetHashCode();
			key = key * 37 + id.IdentifierType.GetHashCode();
			return key;
		}
        public ActionDefinition ActionDefinition
        {
            get
            {
                return this.mDefinition;
            }
        }
        public ReadOnlyCollection<AtomicFormula> Preconditions
        {
            get
            {
                return new ReadOnlyCollection<AtomicFormula>(this.mPreconditions.Values.ToList());
            }
        }
        public ReadOnlyCollection<AtomicFormula> AddEffects
        {
            get
            {
                return new ReadOnlyCollection<AtomicFormula>(this.mAddEffects.Values.ToList());
            }
        }
        public ReadOnlyCollection<AtomicFormula> DeleteEffects
        {
            get
            {
                return new ReadOnlyCollection<AtomicFormula>(this.mDeleteEffects.Values.ToList());
            }
        }
        public ReadOnlyCollection<Identifier> Identifiers
        {
            get
            {
                return new ReadOnlyCollection<Identifier>(this.mIdentifiers.Values.ToList());
            }
        }
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            else if (obj.GetType() == typeof(Action))
                return this == (Action)obj;
            else
                return false;
        }

        public static bool operator ==(Action x, Action y)
        {
            if (System.Object.ReferenceEquals(x, y))
                return true;
            else if ((object)x == null || (object)y == null)
                return false;
            else
                return x.GetHashCode() == y.GetHashCode();

        }

        public static bool operator !=(Action x, Action y)
        {
            return !(x == y);
        }

        public override int GetHashCode()
        {
            if (this.mHashCode == null)
            {
                int result = 17;
                result = result * 37 + this.mDefinition.GetHashCode();
                for (int i = 0; i < this.mObjects.Count; i++)
                    result = result * 37 + this.mObjects[i].GetHashCode();
                this.mHashCode = result;
            }
            return this.mHashCode.Value;
        }
        public override string ToString()
        {
            string t = string.Empty;
            t = "(" + this.mDefinition.Name + " ";
            foreach (PddlObject obj in this.mObjects)
                t += obj.Name + " ";
            t = t.Trim() + ")";
            return t;
        }
		public bool ContainsIdentifier(Identifier id)
		{
			return this.mIdentifiers.ContainsKey(id);	
		}
		public bool ContainsAddEffect(AtomicFormula af)
		{
			return this.mAddEffects.ContainsKey(af);
		}
		public bool ContainsDeleteEffect(AtomicFormula af)
		{
			return this.mDeleteEffects.ContainsKey(af);	
		}
		public bool ContainsPrecondition(AtomicFormula af)
		{
			return this.mPreconditions.ContainsKey(af);	
		}
		public List<Identifier> GetMatching(Identifier id)
		{
			int key = this.GetKey(id);
			if (this.mIdentifierMatches.ContainsKey(key))
				return this.mIdentifierMatches[key];
			else
				return null;
		}
		public bool IsMutex(Action a)
		{
			bool mutex = this.mDeleteEffects.Keys.Any(af =>
							a.ContainsAddEffect(af) ||
								a.ContainsPrecondition(af));
			if(!mutex)
			{
				mutex = a.mDeleteEffects.Keys.Any(af =>
							this.ContainsAddEffect(af) ||
							this.ContainsPrecondition(af));
			}
			return mutex;
		}
    }
}
